package imessage

import (
	"fmt"
	mux "github.com/julienschmidt/httprouter"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

type IMGatewayServer struct {
	http_server *HttpServer
	tcp_server  *TCPServer
	router      *Router
	store       *IMStorage
	NodeId      string
	MaxMemory   int64
}

func (s *IMGatewayServer) ListenAndServe() error {
	s.router = new_router()
	if store, err := NewIMStorage(GetString("main", "work_dir", "work") + "/imdb.db"); err != nil {
		syslog.Println(err)
		return err
	} else {
		s.store = store
	}
	s.NodeId = GetString("main", "node_id", "abcd")
	s.MaxMemory = int64(GetInt("main", "max_memory", 33554432))

	ll, e := net.Listen("tcp", GetString("main", "listen", ":8780"))
	if e != nil {
		panic(e)
	}
	host, port, e := net.SplitHostPort(ll.Addr().String())
	if e != nil {
		panic(e)

	}
	p, e := strconv.Atoi(port)
	if e != nil {
		panic(e)
	}

	syslog.Println(host, port, p)
	tll, e := net.Listen("tcp", fmt.Sprintf(":%v", p+1))
	if e != nil {
		panic(e)
	}
	s.http_server = &HttpServer{listener: ll, gws: s}
	go s.http_server.ListenAndServe()
	s.tcp_server = &TCPServer{listener: tll, gws: s}
	return s.tcp_server.ListenAndServe()

}

type HttpServer struct {
	listener net.Listener
	gws      *IMGatewayServer
}
type TCPServer struct {
	listener net.Listener
	gws      *IMGatewayServer
}

func (hs *HttpServer) ListenAndServe() error {
	router := mux.New()
	router.POST("/im/member/login", hs.gws.on_member_login)
	router.POST("/im/member/logout", hs.gws.on_member_logout)
	router.POST("/im/msg/send", hs.gws.on_send_msg)
	router.POST("/im/msg/forward", hs.gws.on_forward_msg)
	router.GET("/im/msg/recv", hs.gws.on_recv_msg)
	router.POST("/im/group/create", hs.gws.on_group_create)
	router.POST("/im/group/rm", hs.gws.on_group_remove)
	router.POST("/im/group/join", hs.gws.on_group_join)
	router.POST("/im/group/leave", hs.gws.on_group_leave)

	router.NotFound = forbidden
	router.PanicHandler = internal_error
	server := http.Server{
		ReadTimeout:    3600 * time.Second,
		WriteTimeout:   3600 * time.Second,
		MaxHeaderBytes: 1 << 20,
		Handler:        router,
	}
	server.SetKeepAlivesEnabled(true)
	syslog.Println("Http Server Listen at " + hs.listener.Addr().String())
	return server.Serve(hs.listener)
}

func (srv *TCPServer) ListenAndServe() error {
	l := srv.listener.(*net.TCPListener)
	defer l.Close()

	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		grw, e := l.AcceptTCP()
		if e != nil {
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				syslog.Println("tcp: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		tempDelay = 0
		grw.SetKeepAlive(true)
		grw.SetKeepAlivePeriod(3 * time.Minute)
		go serve(srv, grw)
	}
}

func serve(srv *TCPServer, rw *net.TCPConn) {
	defer rw.Close()
	b := make([]byte, 64)
	i, e := rw.Read(b)
	if e != nil {
		syslog.Println("tcp server error", e)
		return
	}
	sid := strings.TrimSpace(string(b[0:i]))
	if session, has := srv.gws.router.sids[sid]; has {
		session.Active()
		session.Online()
		//sig := make(chan bool)
		go func() {
			for {
				msg, more := <-session.channel
				if !more {
					syslog.Println("session", sid, "offline")
					return
				} else {
					srv.gws.router.Forward(msg)
				}
			}
		}()
		for {
			rw.SetReadDeadline(time.Now().Add(60 * time.Second))
			if _, e := rw.Read(b); e != nil && isTimeout(e) {
				syslog.Println("session", sid, e)
				session.Offline()
				return
			}
			rw.SetWriteDeadline(time.Now().Add(5 * time.Second))
			if _, e := rw.Write(b[0:i]); e != nil {
				syslog.Println("session", sid, e)
				session.Offline()
				return
			}
		}
	}
}

func isTimeout(err error) bool {
	e, ok := err.(net.Error)
	return ok && e.Timeout()
}

type Session struct {
	member         string
	sid            string
	lastActiveTime time.Time
	loginTime      time.Time
	deviceId       string
	server         *IMGatewayServer
	channel        chan *Message
	online         bool
	lock           sync.Mutex
}

func (s *Session) IsOnline() bool {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s.online
}
func (s *Session) Online() {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.online = true
	s.channel = make(chan *Message)
}

func (s *Session) Offline() {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.online = false
	close(s.channel)
}

func (s *Session) Active() {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.lastActiveTime = time.Now()
}

func (s *Session) Send(msg *Message) bool {
	if s.IsOnline() {
		s.channel <- msg
		return true
	}
	return false
}
